package test.base.medium.ffmpeg;

import static org.bytedeco.ffmpeg.global.avcodec.*;

import static org.bytedeco.ffmpeg.global.avformat.*;

import static org.bytedeco.ffmpeg.global.avutil.*;
import static org.bytedeco.ffmpeg.global.swresample.swr_alloc_set_opts;
import static org.bytedeco.ffmpeg.global.swresample.swr_convert;
import static org.bytedeco.ffmpeg.global.swresample.swr_free;
import static org.bytedeco.ffmpeg.global.swresample.swr_init;
import static org.bytedeco.ffmpeg.global.swscale.sws_freeContext;

import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.bytedeco.ffmpeg.avcodec.AVCodec;
import org.bytedeco.ffmpeg.avcodec.AVCodecContext;
import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVOutputFormat;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.avutil.AVDictionary;
import org.bytedeco.ffmpeg.avutil.AVFrame;
import org.bytedeco.ffmpeg.avutil.AVRational;
import org.bytedeco.ffmpeg.swresample.SwrContext;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.DoublePointer;
import org.bytedeco.javacpp.FloatPointer;
import org.bytedeco.javacpp.IntPointer;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.PointerPointer;
import org.bytedeco.javacpp.ShortPointer;
import org.bytedeco.javacv.FrameRecorder.Exception;

public class ACAudio {
	ACBase 输出引擎;
	// 输出格式上下文
	AVFormatContext 输出设备;
	// 输出流
	AVStream 输出流 = null;
	// 编码
	AVCodec 编码 = null;
	// 编码器
	AVCodecContext 编码器 = null;

	AVOutputFormat 输出格式;
	AVStream 输入流;
	int codecid = 0;
	String audioCodecName;

	AVRational av帧率;
	AVRational av时间基线;

	int ret = 0;
	BytePointer 音频输出缓存;
	BytePointer 输出样本[];
	Pointer 输入样本[];
	PointerPointer samples_in_ptr;
	PointerPointer samples_out_ptr;
	AVFrame frame = null;
	private AVPacket audio_pkt;
	int[] got_audio_packet = new int[1];

	private SwrContext samples_convert_ctx;
	protected Map<String, String> 自定义属性 = new HashMap<String, String>();
	protected Map<String, String> 自定义元数据 = new HashMap<String, String>();
	int audio_input_frame_size = 0;
	int audio_outbuf_size = 256 * 1024;

	public ACAudio do1_创建并配置(ACBase 输出引擎, AVStream 输入流) throws Exception {
		if (输入流 == null)
			return this;
		this.输出引擎 = 输出引擎;
		this.输出设备 = 输出引擎.输出设备;
		输出格式 = 输出设备.oformat();
		this.输入流 = 输入流;
		AVCodecParameters inparam = 输入流.codecpar();
		codecid = inparam.codec_id();
		String format_name = 输出格式.name().getString();
		if (codecid != AV_CODEC_ID_NONE) {
			输出格式.audio_codec(codecid);
		} else if ("flv".equals(format_name) || "mp4".equals(format_name) || "3gp".equals(format_name)) {
			输出格式.audio_codec(AV_CODEC_ID_AAC);
		} else if ("avi".equals(format_name)) {
			输出格式.audio_codec(AV_CODEC_ID_PCM_S16LE);
		}
		/* 找到音频编码器 */
		if ((编码 = avcodec_find_encoder_by_name(audioCodecName)) == null
				&& (编码 = avcodec_find_encoder(输出格式.audio_codec())) == null) {

			error("avcodec_find_encoder() error: 没发现音频编码:" + 编码);
		}
		if ((编码器 = avcodec_alloc_context3(编码)) == null) {

			error("avcodec_alloc_context3() 错误: 不能分配音频编码器内存");
		}

		输出格式.audio_codec(编码.id());

		if ((输出流 = avformat_new_stream(输出设备, null)) == null) {
			// releaseUnsafe();
			error("avformat_new_stream() error: 不能分配音频流内存");
		}
		// 编码器内存声明

		// 编码器初始化
		// 该函数已经完成了大部分的赋值工作,不需要自己再赋值,除非有修改

		if (avcodec_parameters_to_context(编码器, 输入流.codecpar()) < 0) {
			// if ((ret = avcodec_copy_context(输出流.codec(), 输入流.codec())) <
			// 0) {
			error("avcodec_parameters_to_context() 错误:\t从输入流无法复制参数到上下文\n");
		}
//     		AVCodecParameters ap=输入流.codecpar();
//     		ap	.bit_rate();
//     		ap.sample_rate();
//     		ap.channels();
//     		ap.format();
//             audioQuality = 输入流.codec().global_quality();
		编码器.codec_tag(0);
//             输出流.pts(输入流.pts());
		输出流.duration(输入流.duration());
		输出流.time_base().num(输入流.time_base().num());
		输出流.time_base().den(输入流.time_base().den());

		编码器.codec_id(输出格式.audio_codec());
		编码器.codec_type(AVMEDIA_TYPE_AUDIO);
		return this;
	}

	public ACAudio set比特率(int 比特率) {
		if (比特率 >= 0) {
			编码器.bit_rate(比特率);
		}
		return this;
	}

	public void do2_分配工作内存() throws Exception {

		AVDictionary options = new AVDictionary(null);
//            if (audioQuality >= 0) {
//                av_dict_set(options, "crf", "" + audioQuality, 0);
//            }
		for (Entry<String, String> e : 自定义属性.entrySet()) {
			av_dict_set(options, e.getKey(), e.getValue(), 0);
		}

		// 启用多线程
		编码器.thread_count(0);

		/* 打开编码器 */
		if ((ret = avcodec_open2(编码器, 编码, options)) < 0) {

			av_dict_free(options);
			error("avcodec_open2() error " + ret + ": 不能打开编码器");
		}
		av_dict_free(options);

		音频输出缓存 = new BytePointer(av_malloc(audio_outbuf_size));

		// 设定帧大小
		if (编码器.frame_size() <= 1) {
			audio_outbuf_size = AV_INPUT_BUFFER_MIN_SIZE;
			audio_input_frame_size = audio_outbuf_size / 编码器.channels();
			switch (编码器.codec_id()) {
			case AV_CODEC_ID_PCM_S16LE:
			case AV_CODEC_ID_PCM_S16BE:
			case AV_CODEC_ID_PCM_U16LE:
			case AV_CODEC_ID_PCM_U16BE:
				audio_input_frame_size >>= 1;
				break;
			default:
				break;
			}
		} else {
			audio_input_frame_size = 编码器.frame_size();
		}
		// int bufferSize = audio_input_frame_size * 编码器.bits_per_raw_sample()/8 *
		// 编码器.channels();
		/**
		 * 分片 1：无论是不是分片的数据总量是相同的.
		 * 2：分片的存储在内存中linesize如果两声道则左右分开占用linesize[0]和linesize[1].
		 * 3：不是分片的存储在内存中两声道不分开，左右左右....这样存储，只占用linesize[0].
		 */
		int planes = av_sample_fmt_is_planar(编码器.sample_fmt()) != 0 ? (int) 编码器.channels() : 1;
		int data_size = av_samples_get_buffer_size((IntPointer) null, 编码器.channels(), audio_input_frame_size,
				编码器.sample_fmt(), 1) / planes;
		输出样本 = new BytePointer[planes];
		for (int i = 0; i < 输出样本.length; i++) {
			输出样本[i] = new BytePointer(av_malloc(data_size)).capacity(data_size);
		}
		输入样本 = new Pointer[AVFrame.AV_NUM_DATA_POINTERS];
		samples_in_ptr = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS);
		samples_out_ptr = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS);

		/* 分配音频帧 */
		if ((frame = av_frame_alloc()) == null) {
			error("av_frame_alloc() error: 不能分配");
		}
		frame.pts(0); // magic required by libvorbis and webm

		/*
		 * 根据提供的codeccontext中的值填充parameters结构。 par中分配的任何字段都将被释放并替换为编解码器中相应字段的副本。
		 */
		if ((ret = avcodec_parameters_from_context(输出流.codecpar(), 编码器)) < 0) {
			error("avcodec_parameters_from_context() error: 不能拷贝参数");
		}

		AVDictionary metadata = new AVDictionary(null);
		for (Entry<String, String> e : 自定义元数据.entrySet()) {
			av_dict_set(metadata, e.getKey(), e.getValue(), 0);
		}
		输出流.metadata(metadata);

	}

	public boolean recordSamples(Buffer... samples) throws Exception {
		return recordSamples(0, 0, samples);
	}

	public boolean recordSamples(int sampleRate, int audioChannels, Buffer... 样本集) throws Exception {
		if (输出流 == null) {
			error("没有音频输出流");
		}
		if (!this.输出引擎.启动) {
			error("引擎已经关闭");
		}

		if (样本集 == null && 输出样本[0].position() > 0) {
			// Typically samples_out[0].limit() is double the audio_input_frame_size -->
			// sampleDivisor = 2
			double sampleDivisor = Math
					.floor((int) Math.min(输出样本[0].limit(), Integer.MAX_VALUE) / audio_input_frame_size);
			writeSamples((int) Math.floor((int) 输出样本[0].position() / sampleDivisor));
			return record((AVFrame) null);
		}

		int ret;

		if (sampleRate <= 0) {
			sampleRate = 编码器.sample_rate();
		}
		if (audioChannels <= 0) {
			audioChannels = 编码器.channels();
		}
		int inputSize = 样本集 != null ? 样本集[0].limit() - 样本集[0].position() : 0;
		int inputFormat = 输入流.codecpar().format();
		int inputChannels = 样本集 != null && 样本集.length > 1 ? 1 : audioChannels;
		int inputDepth = 0;
		int outputFormat = 编码器.sample_fmt();
		int outputChannels = 输出样本.length > 1 ? 1 : 编码器.channels();
		int outputDepth = av_get_bytes_per_sample(outputFormat);
		// 根据音频采样格式,获取输入样本
		if (样本集 != null && 样本集[0] instanceof ByteBuffer) {
			inputFormat = 样本集.length > 1 ? AV_SAMPLE_FMT_U8P : AV_SAMPLE_FMT_U8;
			inputDepth = 1;
			for (int i = 0; i < 样本集.length; i++) {
				ByteBuffer b = (ByteBuffer) 样本集[i];
				if (输入样本[i] instanceof BytePointer && 输入样本[i].capacity() >= inputSize && b.hasArray()) {
					((BytePointer) 输入样本[i]).position(0).put(b.array(), b.position(), inputSize);
				} else {
					输入样本[i] = new BytePointer(b);
				}
			}
		} else if (样本集 != null && 样本集[0] instanceof ShortBuffer) {
			inputFormat = 样本集.length > 1 ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16;
			inputDepth = 2;
			for (int i = 0; i < 样本集.length; i++) {
				ShortBuffer b = (ShortBuffer) 样本集[i];
				if (输入样本[i] instanceof ShortPointer && 输入样本[i].capacity() >= inputSize && b.hasArray()) {
					((ShortPointer) 输入样本[i]).position(0).put(b.array(), 样本集[i].position(), inputSize);
				} else {
					输入样本[i] = new ShortPointer(b);
				}
			}
		} else if (样本集 != null && 样本集[0] instanceof IntBuffer) {
			inputFormat = 样本集.length > 1 ? AV_SAMPLE_FMT_S32P : AV_SAMPLE_FMT_S32;
			inputDepth = 4;
			for (int i = 0; i < 样本集.length; i++) {
				IntBuffer b = (IntBuffer) 样本集[i];
				if (输入样本[i] instanceof IntPointer && 输入样本[i].capacity() >= inputSize && b.hasArray()) {
					((IntPointer) 输入样本[i]).position(0).put(b.array(), 样本集[i].position(), inputSize);
				} else {
					输入样本[i] = new IntPointer(b);
				}
			}
		} else if (样本集 != null && 样本集[0] instanceof FloatBuffer) {
			inputFormat = 样本集.length > 1 ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT;
			inputDepth = 4;
			for (int i = 0; i < 样本集.length; i++) {
				FloatBuffer b = (FloatBuffer) 样本集[i];
				if (输入样本[i] instanceof FloatPointer && 输入样本[i].capacity() >= inputSize && b.hasArray()) {
					((FloatPointer) 输入样本[i]).position(0).put(b.array(), b.position(), inputSize);
				} else {
					输入样本[i] = new FloatPointer(b);
				}
			}
		} else if (样本集 != null && 样本集[0] instanceof DoubleBuffer) {
			inputFormat = 样本集.length > 1 ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL;
			inputDepth = 8;
			for (int i = 0; i < 样本集.length; i++) {
				DoubleBuffer b = (DoubleBuffer) 样本集[i];
				if (输入样本[i] instanceof DoublePointer && 输入样本[i].capacity() >= inputSize && b.hasArray()) {
					((DoublePointer) 输入样本[i]).position(0).put(b.array(), b.position(), inputSize);
				} else {
					输入样本[i] = new DoublePointer(b);
				}
			}
		} else if (样本集 != null) {
			error("音频采样不支持样本格式: " + 样本集);
		}
//实例化转换器
		if (samples_convert_ctx == null) {
			samples_convert_ctx = swr_alloc_set_opts(samples_convert_ctx, 编码器.channel_layout(), outputFormat,
					编码器.sample_rate(), av_get_default_channel_layout(audioChannels), inputFormat, sampleRate, 0, null);
			if (samples_convert_ctx == null) {
				error("swr_alloc_set_opts() error: 不能声明音频转换器");
			} else if ((ret = swr_init(samples_convert_ctx)) < 0) {
				error("swr_init() error " + ret + ": 不能初始化音频转换器.");
			}
//	            samples_channels = audioChannels;
//	            samples_format = inputFormat;
//	            samples_rate = sampleRate;
		}

		for (int i = 0; 样本集 != null && i < 样本集.length; i++) {
			输入样本[i].position(输入样本[i].position() * inputDepth).limit((输入样本[i].position() + inputSize) * inputDepth);
		}
		// 转换输入样本到输出样本
		while (true) {
			int inputCount = (int) Math.min(
					样本集 != null ? (输入样本[0].limit() - 输入样本[0].position()) / (inputChannels * inputDepth) : 0,
					Integer.MAX_VALUE);
			int outputCount = (int) Math.min((输出样本[0].limit() - 输出样本[0].position()) / (outputChannels * outputDepth),
					Integer.MAX_VALUE);
			inputCount = Math.min(inputCount, (outputCount * sampleRate + 编码器.sample_rate() - 1) / 编码器.sample_rate());
			for (int i = 0; 样本集 != null && i < 样本集.length; i++) {
				samples_in_ptr.put(i, 输入样本[i]);
			}
			for (int i = 0; i < 输出样本.length; i++) {
				samples_out_ptr.put(i, 输出样本[i]);
			}
			if ((ret = swr_convert(samples_convert_ctx, samples_out_ptr, outputCount, samples_in_ptr,
					inputCount)) < 0) {
				error("swr_convert() error " + ret + ": 不能转换样本格式");
			} else if (ret == 0) {
				break;
			}
			for (int i = 0; 样本集 != null && i < 样本集.length; i++) {
				输入样本[i].position(输入样本[i].position() + inputCount * inputChannels * inputDepth);
			}
			for (int i = 0; i < 输出样本.length; i++) {
				输出样本[i].position(输出样本[i].position() + ret * outputChannels * outputDepth);
			}

			if (样本集 == null || 输出样本[0].position() >= 输出样本[0].limit()) {
				writeSamples(audio_input_frame_size);
			}
		}
		return 样本集 != null ? frame.key_frame() != 0 : record((AVFrame) null);
	}

	private void writeSamples(int nb_samples) throws Exception {
		if (输出样本 == null || 输出样本.length == 0) {
			return;
		}
		// 设置音频帧大小
		frame.nb_samples(nb_samples);
		avcodec_fill_audio_frame(frame, 编码器.channels(), 编码器.sample_fmt(), 输出样本[0], (int) 输出样本[0].position(), 0);
		for (int i = 0; i < 输出样本.length; i++) {
			int linesize = 0;
			if (输出样本[0].position() > 0 && 输出样本[0].position() < 输出样本[0].limit()) {
				// align the end of the buffer to a 32-byte boundary as sometimes required by
				// FFmpeg
				linesize = ((int) 输出样本[i].position() + 31) & ~31;
			} else {
				linesize = (int) Math.min(输出样本[i].limit(), Integer.MAX_VALUE);
			}

			frame.data(i, 输出样本[i].position(0));
			frame.linesize(i, linesize);
		}
		frame.quality(编码器.global_quality());
		record(frame);
	}

	boolean record(AVFrame frame) throws Exception {
		int ret;

		av_init_packet(audio_pkt);
		audio_pkt.data(音频输出缓存);
		audio_pkt.size(audio_outbuf_size);
//		if ((ret = avcodec_encode_audio2(编码器, audio_pkt, frame, got_audio_packet)) < 0) {
//			error("avcodec_encode_audio2() error " + ret + ": Could not encode audio packet.");
//		}
		if (frame != null) {
			frame.pts(frame.pts() + frame.nb_samples()); // magic required by libvorbis and webm
		}
		if (0 == avcodec_send_frame(编码器, frame))
			if (avcodec_receive_packet(编码器, audio_pkt) == 0) {
				if (audio_pkt.pts() != AV_NOPTS_VALUE) {
					// a * bq / cq
					// a / 90000 * 25
					audio_pkt.pts(av_rescale_q(audio_pkt.pts(), 编码器.time_base(), 输出流.time_base()));
				}
				if (audio_pkt.dts() != AV_NOPTS_VALUE) {
					audio_pkt.dts(av_rescale_q(audio_pkt.dts(), 编码器.time_base(), 输出流.time_base()));
				}
				audio_pkt.flags(audio_pkt.flags() | AV_PKT_FLAG_KEY);
				audio_pkt.stream_index(输出流.index());
				/* write the compressed frame in the media file */
				输出引擎.writePacket(AVMEDIA_TYPE_AUDIO, audio_pkt);
				return true;
			}

		return false;

	}

	public ACAudio set帧率(int sampleRate) {
		// AVRational sample_rate = av_d2q(sampleRate, 1001000);
		if (sampleRate >= 0) {
			编码器.sample_rate(sampleRate);
			// 求精度
			av帧率 = av_d2q(sampleRate, 1001000);
			// 求倒数
			AVRational time_base = av_inv_q(av帧率);
			编码器.time_base(time_base);
			输出流.time_base(time_base);
			输出流.codec().time_base(time_base); // 必须有,否则异常
		}
		return this;
	}

	public ACAudio set通道数(int audioChannels) {
		if (audioChannels >= 0) {
			编码器.channels(audioChannels);
			编码器.channel_layout(av_get_default_channel_layout(audioChannels));
		}
		return this;
	}

	public ACAudio set采样格式(int sampleFormat) {
		if (sampleFormat != AV_SAMPLE_FMT_NONE) {
			编码器.sample_fmt(sampleFormat);
		} else {
			// use AV_SAMPLE_FMT_S16 by default, if available
			编码器.sample_fmt(AV_SAMPLE_FMT_FLTP);
			IntPointer formats = 编码器.codec().sample_fmts();
			for (int i = 0; formats.get(i) != -1; i++) {
				if (formats.get(i) == AV_SAMPLE_FMT_S16) {
					编码器.sample_fmt(AV_SAMPLE_FMT_S16);
					break;
				}
			}
		}
		switch (编码器.sample_fmt()) {
		case AV_SAMPLE_FMT_U8:
		case AV_SAMPLE_FMT_U8P:
			编码器.bits_per_raw_sample(8);
			break;
		case AV_SAMPLE_FMT_S16:
		case AV_SAMPLE_FMT_S16P:
			编码器.bits_per_raw_sample(16);
			break;
		case AV_SAMPLE_FMT_S32:
		case AV_SAMPLE_FMT_S32P:
			编码器.bits_per_raw_sample(32);
			break;
		case AV_SAMPLE_FMT_FLT:
		case AV_SAMPLE_FMT_FLTP:
			编码器.bits_per_raw_sample(32);
			break;
		case AV_SAMPLE_FMT_DBL:
		case AV_SAMPLE_FMT_DBLP:
			编码器.bits_per_raw_sample(64);
			break;
		default:
			assert false;
		}
		return this;
	}

	public ACAudio set音频质量(int audioQuality) {
		if (audioQuality >= 0) {
			编码器.flags(编码器.flags() | AV_CODEC_FLAG_QSCALE);
			编码器.global_quality((int) Math.round(FF_QP2LAMBDA * audioQuality));
		}
		return this;
	}

	public ACAudio do优化参数(int audioQuality) {

		// some formats want stream headers to be separate
		if ((输出格式.flags() & AVFMT_GLOBALHEADER) != 0) {
			编码器.flags(编码器.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
		}
		return this;
	}

	public ACAudio enable非标编码() {
		if ((编码.capabilities() & AV_CODEC_CAP_EXPERIMENTAL) != 0) {
			编码器.strict_std_compliance(AVCodecContext.FF_COMPLIANCE_EXPERIMENTAL);
		}
		return this;
	}

	public void error(String 异常) throws Exception {
		free();
		throw new Exception(异常);
	}

	public void free() {

		if (samples_convert_ctx != null) {
			swr_free(samples_convert_ctx);
			samples_convert_ctx = null;
		}
		if (frame != null) {
			av_frame_free(frame);
			frame = null;
		}
		if (编码器 != null) {
			avcodec_free_context(编码器);
			编码器 = null;
		}
		if (输出样本 != null) {
			for (int i = 0; i < 输出样本.length; i++) {
				av_free(输出样本[i].position(0));
			}
			输出样本 = null;
		}
		if (音频输出缓存 != null) {
			av_free(音频输出缓存);
			音频输出缓存 = null;
		}
		if (输出流 != null && 输出流.metadata() != null) {
			av_dict_free(输出流.metadata());
			输出流.metadata(null);
		}
	}

//	 public void releaseUnsafe() throws Exception {
//	        started = false;
//
//	        /* close each codec */
//	        if (video_c != null) {
//	            avcodec_free_context(video_c);
//	            video_c = null;
//	        }
//	        if (编码器 != null) {
//	            avcodec_free_context(编码器);
//	            编码器 = null;
//	        }
//	        if (picture_buf != null) {
//	            av_free(picture_buf);
//	            picture_buf = null;
//	        }
//	        if (picture != null) {
//	            av_frame_free(picture);
//	            picture = null;
//	        }
//	        if (tmp_picture != null) {
//	            av_frame_free(tmp_picture);
//	            tmp_picture = null;
//	        }
//	        if (video_outbuf != null) {
//	            av_free(video_outbuf);
//	            video_outbuf = null;
//	        }
//	        if (frame != null) {
//	            av_frame_free(frame);
//	            frame = null;
//	        }
//	        if (samples_out != null) {
//	            for (int i = 0; i < samples_out.length; i++) {
//	                av_free(samples_out[i].position(0));
//	            }
//	            samples_out = null;
//	        }
//	        if (audio_outbuf != null) {
//	            av_free(audio_outbuf);
//	            audio_outbuf = null;
//	        }
//	        if (video_st != null && video_st.metadata() != null) {
//	            av_dict_free(video_st.metadata());
//	            video_st.metadata(null);
//	        }
//	        if (输出流 != null && 输出流.metadata() != null) {
//	            av_dict_free(输出流.metadata());
//	            输出流.metadata(null);
//	        }
//	        video_st = null;
//	        输出流 = null;
//	        filename = null;
//
//	        AVFormatContext outputStreamKey = oc;
//	        if (oc != null && !oc.isNull()) {
//	            if (outputStream == null && (oformat.flags() & AVFMT_NOFILE) == 0) {
//	                /* close the output file */
//	                avio_close(oc.pb());
//	            }
//
//	            /* free the streams */
//	            int nb_streams = oc.nb_streams();
//	            for (int i = 0; i < nb_streams; i++) {
//	                av_free(oc.streams(i).codec());
//	                av_free(oc.streams(i));
//	            }
//
//	            /* free metadata */
//	            if (oc.metadata() != null) {
//	                av_dict_free(oc.metadata());
//	                oc.metadata(null);
//	            }
//
//	            /* free the stream */
//	            av_free(oc);
//	            oc = null;
//	        }
//
//	        if (img_convert_ctx != null) {
//	            sws_freeContext(img_convert_ctx);
//	            img_convert_ctx = null;
//	        }
//
//	        if (samples_convert_ctx != null) {
//	            swr_free(samples_convert_ctx);
//	            samples_convert_ctx = null;
//	        }
//
//	        if (outputStream != null) {
//	            try {
//	                if (closeOutputStream) {
//	                    outputStream.close();
//	                }
//	            } catch (IOException ex) {
//	                error("Error on OutputStream.close(): ", ex);
//	            } finally {
//	                outputStream = null;
//	                outputStreams.remove(outputStreamKey);
//	                if (avio != null) {
//	                    if (avio.buffer() != null) {
//	                        av_free(avio.buffer());
//	                        avio.buffer(null);
//	                    }
//	                    av_free(avio);
//	                    avio = null;
//	                }
//	            }
//	        }
//	    }
//	    @Override protected void finalize() throws Throwable {
//	        super.finalize();
//	        release();
//	    }
}
